﻿using SketchUpAPI.NET.Enums;
using SketchUpAPI.NET.SketchUp.Exception;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel.Design;

namespace SketchUpAPI.NET.Objects
{
    public class GeometryInput : ObjectBase
    {
        #region API 函数
        /// <summary>
        /// 创建一个 GeometryInput
        /// </summary>
        /// <param name="geom_input">GeometryInput</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputCreate(out IntPtr geom_input);

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="geom_input">GeometryInput</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputRelease(ref IntPtr geom_input);

        /// <summary>
        /// 添加顶点
        /// </summary>
        /// <param name="geom_input">GeometryInput</param>
        /// <param name="point">点坐标</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputAddVertex(IntPtr geom_input, ref Point3D point);

        /// <summary>
        /// 设置全部顶点，已有的顶点将被删除
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="num_vertices">顶点个数</param>
        /// <param name="points">全部顶点坐标</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputSetVertices(IntPtr geom_input, long num_vertices, Point3D[] points);

        /// <summary>
        /// 添加边缘，此方法用于添加与 LoopInput 无关的边，如果需要添加面上的边应使用 LoopInput 相关方法
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="vertex0_index">起点的索引</param>
        /// <param name="vertex1_index">终点索引</param>
        /// <param name="added_edge_index">被创建出来的边的索引</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputAddEdge(IntPtr geom_input, int vertex0_index, int vertex1_index, out int added_edge_index);

        /// <summary>
        /// 设置某个边是否隐藏
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="edge_index">边的索引</param>
        /// <param name="hidden">是否隐藏</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputEdgeSetHidden(IntPtr geom_input, long edge_index, bool hidden);

        /// <summary>
        /// 设置某个边是否软化
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="edge_index">边的索引</param>
        /// <param name="soft">是否软化</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputEdgeSetSoft(IntPtr geom_input, long edge_index, bool soft);

        /// <summary>
        /// 设置某个边是否柔滑
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="edge_index">边的索引</param>
        /// <param name="smooth">是否柔滑</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputEdgeSetSmooth(IntPtr geom_input, long edge_index, bool smooth);

        /// <summary>
        /// 设置边的材质
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="edge_index">边的索引</param>
        /// <param name="material"><see cref="Material"/> 材质</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputEdgeSetMaterial(IntPtr geom_input, long edge_index, IntPtr material);

        /// <summary>
        /// 设置边的图层
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="edge_index">边的索引</param>
        /// <param name="layer"><see cref="Layer"/> 图层</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputEdgeSetLayer(IntPtr geom_input, long edge_index, IntPtr layer);

        /// <summary>
        /// 添加曲线，此方法用于指定与 LoopInput 无关的曲线。需要指定面上的曲线，应使用 LoopInput 相关方法
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="num_edges">边的个数</param>
        /// <param name="edge_indices">边的索引</param>
        /// <param name="added_curve_index">返回，曲线索引</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputAddCurve(IntPtr geom_input, int num_edges, int[] edge_indices, out int added_curve_index);

        /// <summary>
        /// 添加圆弧
        /// </summary>
        /// <remarks>该方法同时会将圆弧的中间顶点添加到该实例中，因此如果想将这个圆弧添加到一个<see cref="Loop"/>中，只需要添加顶点索引即可</remarks>
        /// <param name="geom_input"></param>
        /// <param name="start_point">起点</param>
        /// <param name="end_point">终点</param>
        /// <param name="center">圆心</param>
        /// <param name="normal">法向</param>
        /// <param name="num_segments">分段数</param>
        /// <param name="added_curve_index">返回，圆弧索引</param>
        /// <param name="control_edge_index">返回，圆弧所控制的边的索引</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputAddArcCurve(IntPtr geom_input, long start_point, long end_point, Point3D center, Vector3D normal, int num_segments, out int added_curve_index, out int control_edge_index);

        /// <summary>
        /// 添加面
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="outer_loop"><see cref="LoopInput"/> 环</param>
        /// <param name="added_face_index">面的索引</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputAddFace(IntPtr geom_input, ref IntPtr outer_loop, out int added_face_index);

        /// <summary>
        /// 反转面的环的方向
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="face_index">面索引</param>
        /// <param name="reverse">是否反转方向</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputFaceSetReverse(IntPtr geom_input, long face_index, bool reverse);

        /// <summary>
        /// 设置面的图层
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="face_index">面的索引</param>
        /// <param name="layer"><see cref="Layer"/> 图层</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputFaceSetLayer(IntPtr geom_input, long face_index, IntPtr layer);

        /// <summary>
        /// 添加内圈环
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="face_index">面的索引</param>
        /// <param name="loop_input"><see cref="LoopInput"/> 内圈环</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputFaceAddInnerLoop(IntPtr geom_input, long face_index, IntPtr loop_input);

        /// <summary>
        /// 设置正面材质
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="face_index">面的索引</param>
        /// <param name="material_input"><see cref="MaterialInput"/> 材质</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputFaceSetFrontMaterial(IntPtr geom_input, long face_index, MaterialInput material_input);

        /// <summary>
        /// 设置背面材质
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="face_index">面的索引</param>
        /// <param name="material_input"><see cref="MaterialInput"/> 材质</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputFaceSetBackMaterial(IntPtr geom_input, long face_index, MaterialInput material_input);

        /// <summary>
        /// 设置面是否隐藏
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="face_index">面的索引</param>
        /// <param name="hidden">是否隐藏</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputFaceSetHidden(IntPtr geom_input, long face_index, bool hidden);

        /// <summary>
        /// 获取各类几何体的个数
        /// </summary>
        /// <param name="geom_input"></param>
        /// <param name="vertices_count">顶点个数</param>
        /// <param name="faces_count">面的个数</param>
        /// <param name="edge_count">边缘个数</param>
        /// <param name="curve_count">曲线个数</param>
        /// <param name="arc_count">圆弧个数</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUGeometryInputGetCounts(IntPtr geom_input, out int vertices_count, out int faces_count, out int edge_count, out int curve_count, out int arc_count);
        #endregion

        /// <summary>
        /// 顶点个数
        /// </summary>
        public int VertexCount
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputGetCounts(Id, out int vertices_count, out _, out _, out _, out _));
                return vertices_count;
            }
        }

        /// <summary>
        /// 面的个数
        /// </summary>
        public int FaceCount
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputGetCounts(Id, out _, out int faces_count, out _, out _, out _));
                return faces_count;
            }
        }

        /// <summary>
        /// 边缘个数
        /// </summary>
        public int EdgeCount
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputGetCounts(Id, out _, out _, out int edge_count, out _, out _));
                return edge_count;
            }
        }

        /// <summary>
        /// 曲线个数
        /// </summary>
        /// <remarks>不包括 <see cref="Arc"/> 的个数</remarks>
        public int CurveCount
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputGetCounts(Id, out _, out _, out _, out int curve_count, out _));
                return curve_count;
            }
        }

        /// <summary>
        /// 圆弧个数
        /// </summary>
        public int ArcCount
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputGetCounts(Id, out _, out _, out _, out _, out int arc_count));
                return arc_count;
            }
        }

        /// <summary>
        /// 创建一个 <see cref="GeometryInput"/>
        /// </summary>
        public GeometryInput()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputCreate(out Id));
        }

        /// <summary>
        /// 添加一个顶点
        /// </summary>
        /// <param name="point">要添加的点</param>
        /// <returns>顶点索引</returns>
        public int AddVertex(Point3D point)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputAddVertex(Id, ref point));
            return VertexCount - 1;
        }

        /// <summary>
        /// 设置全部顶点，已有的顶点将被覆盖
        /// </summary>
        /// <param name="points">全部顶点</param>
        public void SetVertices(params Point3D[] points)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputSetVertices(Id, points.Length, points));
        }

        /// <summary>
        /// 创建一个边缘
        /// </summary>
        /// <param name="vertex0_index">起点索引</param>
        /// <param name="vertex1_index">终点索引</param>
        /// <returns>边缘索引</returns>
        public int AddEdge(int vertex0_index, int vertex1_index)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputAddEdge(Id, vertex0_index, vertex1_index, out int index));
            return index;
        }

        /// <summary>
        /// 添加曲线
        /// </summary>
        /// <param name="edgeIndex"><see cref="Edge"/> 索引</param>
        /// <returns>曲线索引</returns>
        public int AddCurve(int[] edgeIndex)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputAddCurve(Id, edgeIndex.Length, edgeIndex, out int curveIndex));
            return curveIndex;
        }

        /// <summary>
        /// 创建一个圆弧
        /// </summary>
        /// <remarks>该方法同时会将圆弧的中间顶点添加到该实例中，因此如果想将这个圆弧添加到一个<see cref="Loop"/>中，只需要添加顶点索引即可</remarks>
        /// <param name="start">起点 <see cref="Vertex"/> 索引</param>
        /// <param name="end">终点 <see cref="Vertex"/> 索引</param>
        /// <param name="center">圆心</param>
        /// <param name="normal">法向</param>
        /// <param name="num_segmentys">分段数</param>
        /// <param name="startEdgeIndex">圆弧的第一条边缘的索引</param>
        /// <returns>圆弧索引</returns>
        public int AddArc(int start, int end, Point3D center, Vector3D normal, int num_segmentys, out int startEdgeIndex)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputAddArcCurve(Id, start, end, center, normal, num_segmentys, out int arcIndex, out startEdgeIndex));
            return arcIndex;
        }

        /// <summary>
        /// 添加一个面
        /// </summary>
        /// <remarks>如果面添加成功，该 <see cref="LoopInput"/> 将被自动释放</remarks>
        /// <param name="loop"></param>
        /// <returns>面的索引</returns>
        public int AddFace(LoopInput loop)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputAddFace(Id, ref loop.Id, out int index));
            return index;
        }

        /// <summary>
        /// 添加一个面
        /// </summary>
        /// <remarks>该方法会先将点逐个添加到 <see cref="GeometryInput"/> 中，再使用 <see cref="LoopInput"/> 添加一个面</remarks>
        /// <param name="points">面的顶点</param>
        /// <returns>面的索引</returns>
        public int AddFace(params Point3D[] points)
        {
            LoopInput loop = new LoopInput();
            for (int i = 0; i < points.Length; i++)
            {
                int a = AddVertex(points[i]);
                loop.AddVertex(a);
            }

            return AddFace(loop);
        }

        /// <summary>
        /// 使用连续的顶点创建面
        /// </summary>
        /// <param name="startVertex">起始顶点的索引</param>
        /// <param name="vertexCount">顶点总数</param>
        /// <returns>面的索引</returns>
        public int AddFace(int startVertex, int vertexCount)
        {
            LoopInput loop = new LoopInput();
            loop.AddVertex(startVertex, vertexCount);

            return AddFace(loop);
        }

        /// <summary>
        /// 添加一个面
        /// </summary>
        /// <param name="vertexIndex">使用一组顶点添加一个面</param>
        /// <returns>面的索引</returns>
        public int AddFace(params int[] vertexIndex)
        {
            if (vertexIndex.Length < 3) throw new ArgumentException("顶点个数小于三个，无法创建面");
            var index = DistinctAdjacentIndex(vertexIndex);
            if (index.Count < 3) throw new ArgumentException("传入的顶点无法构成一个面");

            LoopInput loop = new LoopInput();
            foreach (var item in index)
            {
                loop.AddVertex(item);
            }

            return AddFace(loop);
        }

        /// <summary>
        /// 添加一个面，同时柔滑软化边缘
        /// </summary>
        /// <param name="vertexIndex"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">无法构成一个面</exception>
        public int AddFaceWithSmoothSoft(params int[] vertexIndex)
        {
            if (vertexIndex.Length < 3) throw new ArgumentException("顶点个数小于三个，无法创建面");
            var index = DistinctAdjacentIndex(vertexIndex);
            if (index.Count < 3) throw new ArgumentException("传入的顶点无法构成一个面");

            LoopInput loop = new LoopInput();
            foreach (var item in index)
            {
                loop.AddVertex(item);
            }

            for (int i = 0; i < index.Count; i++)
            {
                loop.SetEdgeSmooth(i, true);
                loop.SetEdgeSoft(i, true);
            }

            return AddFace(loop);
        }

        /// <summary>
        /// 设置某个边是否隐藏
        /// </summary>
        /// <param name="index">边的索引</param>
        /// <param name="isHidden"></param>
        public void EdgeSetHidden(int index, bool isHidden)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputEdgeSetHidden(Id, index, isHidden));
        }

        /// <summary>
        /// 设置某个边是否软化
        /// </summary>
        /// <param name="index">边的索引</param>
        /// <param name="isSoft"></param>
        public void EdgeSetSoft(int index, bool isSoft)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputEdgeSetSoft(Id, index, isSoft));
        }

        /// <summary>
        /// 设置某个边是否柔滑
        /// </summary>
        /// <param name="index">边的索引</param>
        /// <param name="isSmooth"></param>
        public void EdgeSetSmooth(int index, bool isSmooth)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputEdgeSetSmooth(Id, index, isSmooth));
        }

        /// <summary>
        /// 设置某个边的材质
        /// </summary>
        /// <param name="index"></param>
        /// <param name="material"></param>
        public void EdgeSetMaterial(int index, Material material)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputEdgeSetMaterial(Id, index, material.Id));
        }

        /// <summary>
        /// 设置某个边的图层
        /// </summary>
        /// <param name="index"></param>
        /// <param name="layer"></param>
        public void EdgeSetLayer(int index, Layer layer)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputEdgeSetLayer(Id, index, layer.Id));
        }

        /// <summary>
        /// 设置某个面是否隐藏
        /// </summary>
        /// <param name="index">面的索引</param>
        /// <param name="isHidden"></param>
        public void FaceSetHidden(int index, bool isHidden)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputFaceSetHidden(Id, index, isHidden));
        }

        /// <summary>
        /// 反转面的方向
        /// </summary>
        /// <param name="index"></param>
        /// <param name="isReverse"></param>
        public void FaceSetReverse(int index, bool isReverse)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputFaceSetReverse(Id, index, isReverse));
        }

        /// <summary>
        /// 设置面的图层
        /// </summary>
        /// <param name="index"></param>
        /// <param name="layer"></param>
        public void FaceSetLayer(int index, Layer layer)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputFaceSetLayer(Id, index, layer.Id));
        }

        /// <summary>
        /// 给面添加内圈
        /// </summary>
        /// <param name="index"></param>
        /// <param name="innerloop"></param>
        public void FaceAddInnerLoop(int index, LoopInput innerloop)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputFaceAddInnerLoop(Id, index, innerloop.Id));
        }

        /// <summary>
        /// 设置面的正面材质
        /// </summary>
        /// <param name="index"></param>
        /// <param name="frontmaterial"></param>
        public void FaceSetFrontMaterial(int index, MaterialInput frontmaterial)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputFaceSetFrontMaterial(Id, index, frontmaterial));
        }

        /// <summary>
        /// 设置面的背面材质
        /// </summary>
        /// <param name="index"></param>
        /// <param name="backmaterial"></param>
        public void FaceSetBackMaterial(int index, MaterialInput backmaterial)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputFaceSetBackMaterial(Id, index, backmaterial));
        }

        /// <summary>
        /// 获取各类几何体的个数
        /// </summary>
        /// <param name="vertices_count">顶点 <see cref="Vertex"/></param>
        /// <param name="faces_count">面 <see cref="Face"/></param>
        /// <param name="edge_count">边缘 <see cref="Edge"/></param>
        /// <param name="curve_count">曲线 <see cref="Curve"/></param>
        /// <param name="arc_count">圆弧 <see cref="Arc"/></param>
        public void GetCounts(out int vertices_count, out int faces_count, out int edge_count, out int curve_count, out int arc_count)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputGetCounts(Id, out vertices_count, out faces_count, out edge_count, out curve_count, out arc_count));
        }

        public override void Dispose()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUGeometryInputRelease(ref Id));
        }

        /// <summary>
        /// 去除相邻的索引，且最后一个与第一个也会判断
        /// </summary>
        /// <param name="indexs"></param>
        /// <returns></returns>
        private List<int> DistinctAdjacentIndex(int[] indexs)
        {
            List<int> result = new List<int>();
            foreach (int i in indexs)
            {
                if (result.Count == 0) result.Add(i);
                else if (i == result.Last()) continue;
                else result.Add(i);
            }

            if (result.Count == 1) return result;

            if (result.Last() == result.First()) result.Remove(result.Last());

            return result;
        }
    }
}